package cs.rsa.ts14.kilo;

import cs.rsa.ts14.framework.LineType;
import cs.rsa.ts14.framework.LineTypeClassifierStrategy;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StandardLineTypeClassifierStrategy implements
        LineTypeClassifierStrategy {

    private String[] weekDays = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
    private String[] transportMode = {"Bi", "Ca", "Pu", "Tr", "No", "Ho"};

    private boolean atLeastOneAssignmentLineFound = false;

    private boolean noMoreAssignmentLinesAllowed = false;
    private LineType previousType;

    private int emptyLineCounter = 0;

    @Override
    public LineType classify(String line) {
        if (emptyLineCounter >= 2 || previousType == LineType.INVALID_SEQUENCE) {
            previousType = LineType.INVALID_SEQUENCE;
            return LineType.INVALID_SEQUENCE;
        }
        if (isEmptyLine(line)) {
            emptyLineCounter++;
            previousType = LineType.EMPTY_LINE;
            return LineType.EMPTY_LINE;
        }
        emptyLineCounter = 0;


        if (line.charAt(0) == '#') {
            previousType = LineType.COMMENT_LINE;
            return LineType.COMMENT_LINE;
        }
        if (isAssignmentLine(line)) {
            if (noMoreAssignmentLinesAllowed)
                return LineType.INVALID_SEQUENCE;
            atLeastOneAssignmentLineFound = true;
            previousType = LineType.ASSIGNMENT_LINE;
            return LineType.ASSIGNMENT_LINE;
        }

        String[] items = line.split("(\\s|\\t)+");

        if (atLeastOneAssignmentLineFound) {
            if (isWeekLine(line)) {
                noMoreAssignmentLinesAllowed = true;
                previousType = LineType.WEEK_SPECIFICATION;
                return LineType.WEEK_SPECIFICATION;
            }
            if (items.length >= 2) {
                if (ArrayContainValue(weekDays, items[0]) && ArrayContainValue(transportMode, items[1])) {
                    if (previousType == LineType.WEEK_SPECIFICATION || previousType == LineType.EMPTY_LINE || previousType == LineType.WEEKDAY_SPECIFICATION || previousType == LineType.COMMENT_LINE) {
                        noMoreAssignmentLinesAllowed = true;
                        previousType = LineType.WEEKDAY_SPECIFICATION;
                        return LineType.WEEKDAY_SPECIFICATION;
                    } else {
                        previousType = LineType.INVALID_SEQUENCE;
                        return LineType.INVALID_SEQUENCE;
                    }
                }
            }

            if (isWorkSpecificationLine(line)) {
                if (previousType == LineType.WEEKDAY_SPECIFICATION || previousType == LineType.EMPTY_LINE || previousType == LineType.WORK_SPECIFICATION || previousType == LineType.COMMENT_LINE) {
                    noMoreAssignmentLinesAllowed = true;
                    previousType = LineType.WORK_SPECIFICATION;
                    return LineType.WORK_SPECIFICATION;
                } else {
                    previousType = LineType.INVALID_SEQUENCE;
                    return LineType.INVALID_SEQUENCE;
                }
            }
        } else {
            previousType = LineType.INVALID_SEQUENCE;
            return LineType.INVALID_SEQUENCE;
        }

        previousType = LineType.INVALID_LINE;
        return LineType.INVALID_LINE;
    }

    private boolean isEmptyLine(String line) {

        if (line == null)
            return true;
        Pattern p = Pattern.compile("^(\\s|\\t)*$", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher m = p.matcher(line);
        return m.find();
    }

    private boolean isWorkSpecificationLine(String line) {
        // eks. "	censor	-		7.5";
        String tabOrWs = "(\\s|\\t)+";
        String decimal = "(([+-]?\\d*\\.\\d)|(\\d+))";    // a float/double value with 1 decimal

        Pattern p = Pattern.compile("^" + tabOrWs + "[a-zA-Z]+" + tabOrWs + "([a-zA-Z]+|-)+" + tabOrWs + decimal + "(" + tabOrWs + ".)*$", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher m = p.matcher(line);
        boolean result = m.find();
        return result;
    }

    private boolean isAssignmentLine(String line) {
        String txt = line;
        String tabOrWs = "(\\s|\\t)+";
        String re1 = "((?:[a-z][a-z]+))";    // Word 1
        String re2 = tabOrWs;
        String re3 = "(=)";    // '=' char
        String re4 = tabOrWs;
        String re5 = "(([+-]?\\d*\\.\\d)|(\\d+))";    // a float/double value with 1 decimal

        Pattern p = Pattern.compile("^" + re1 + re2 + re3 + re4 + re5 + "$", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
        Matcher m = p.matcher(txt);
        return m.find();
    }

    public boolean isWeekLine(String line) {
        //"Week 1 : 3 : 0";
        String tabOrWs = "(\\s|\\t)+";
        Pattern p = Pattern.compile("^Week" + tabOrWs + "(([1-9])|([1-4][0-9])|(5[0-3]))" + tabOrWs + ":" + tabOrWs + "[0-5]" + tabOrWs + ":" + tabOrWs + "[0-5]$", Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
//	    System.out.print(p.toString());
        Matcher m = p.matcher(line);
        return m.find();
    }

    private boolean tryParseInt(String value) {
        try {
            Integer.parseInt(value);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    private boolean tryParseDouble(String value) {
        try {
            Double.parseDouble(value);
            return true;
        } catch (NumberFormatException nfe) {
            return false;
        }
    }

    private boolean ArrayContainValue(String[] array, String value) {
        if (array.length == 0 || value.isEmpty())
            return false;

        for (int i = 0; i < array.length; i++) {
            if (array[i].equals(value))
                return true;
        }
        return false;
    }

    @Override
    public String lastError() {
        // TODO Auto-generated method stub
        return null;
    }

}
